
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link href='/css/styles.css' rel='stylesheet' type='text/css' />
    <link href='/images/favicon.png' rel='shortcut icon' />
    <script src='/js/jquery.min.1.4.js'></script>
    <script src='/js/app.js'></script>
    <script src='/js/common.js'></script>
    <meta content='width=device-width, minimum-scale=1.0, maximum-scale=1.0' name='viewport' />
    <title>pub/sub -- Redis中文资料站</title>
	<meta http-equiv="description" content="redis中文资料站，下载安装redis，查找redis常用命令（commands），选择适合的redis客户端方式，配置redis主从（master-slave），阅读redis官方文档，社区里了解更多redis信息，提交redis的bug。" />
  </head>
  <body class=''>
    <script src='/js/head.js'></script>
    <div class='text'>
      <article id='topic'>
        <aside>
          <h2>
            相关命令
          </h2>
          <ul>
            <li>
              <a href='/commands/psubscribe.html'>PSUBSCRIBE</a>
            </li>
            <li>
              <a href='/commands/publish.html'>PUBLISH</a>
            </li>
            <li>
              <a href='/commands/punsubscribe.html'>PUNSUBSCRIBE</a>
            </li>
            <li>
              <a href='/commands/subscribe.html'>SUBSCRIBE</a>
            </li>
            <li>
              <a href='/commands/unsubscribe.html'>UNSUBSCRIBE</a>
            </li>
          </ul>
        </aside>
        <h1>Pub/Sub</h1>
        
        <p>订阅，取消订阅和发布实现了发布/订阅消息范式(引自wikipedia)，发送者（发布者）不是计划发送消息给特定的接收者（订阅者）。而是发布的消息分到不同的频道，不需要知道什么样的订阅者订阅。订阅者对一个或多个频道感兴趣，只需接收感兴趣的消息，不需要知道什么样的发布者发布的。这种发布者和订阅者的解耦合可以带来更大的扩展性和更加动态的网络拓扑。</p>
        
        <p>F为了订阅foo和bar，客户端发出一个订阅的频道名称:</p>
        
        <pre><code>SUBSCRIBE foo bar&#x000A;</code></pre>
        
        <p>其他客户端发到这些频道的消息将会被推送到所有订阅的客户端。</p>
        
        <p>客户端订阅到一个或多个频道不必发出命令，尽管他能订阅和取消订阅其他频道。订阅和取消订阅的响应被封装在发送的消息中，以便客户端只需要读一个连续的消息流，其中第一个元素表示消息类型。</p>
        
        <h2>推送消息的格式</h2>
        
        <p>消息是一个有三个元素的<a href="/topics/protocol.html#multi-bulk-reply">多块响应</a> 。</p>
        
        <p>第一个元素是消息类型:</p>
        
        <ul>
        <li><p><code>subscribe</code>: 表示我们成功订阅到响应的第二个元素提供的频道。第三个参数代表我们现在订阅的频道的数量。</p></li>
        <li><p><code>unsubscribe</code>:表示我们成功取消订阅到响应的第二个元素提供的频道。第三个参数代表我们目前订阅的频道的数量。当最后一个参数是0的时候，我们不再订阅到任何频道。当我们在Pub/Sub以外状态，客户端可以发出任何redis命令。</p></li>
        <li><p><code>message</code>: 这是另外一个客户端发出的发布命令的结果。第二个元素是来源频道的名称，第三个参数是实际消息的内容。</p></li>
        </ul>
        
        
        <h2>报文协议示例</h2>
        
        <pre><code>SUBSCRIBE first second&#x000A;*3&#x000A;$9&#x000A;subscribe&#x000A;$5&#x000A;first&#x000A;:1&#x000A;*3&#x000A;$9&#x000A;subscribe&#x000A;$6&#x000A;second&#x000A;:2&#x000A;</code></pre>
        
        <p>此时，从另一个客户端我们发出关于频道名称为second的发布操作:</p>
        
        <pre><code>&gt; PUBLISH second Hello&#x000A;</code></pre>
        
        <p>这是第一个客户端收到的：</p>
        
        <pre><code>*3&#x000A;$7&#x000A;message&#x000A;$6&#x000A;second&#x000A;$5&#x000A;Hello&#x000A;</code></pre>
        
        <p>现在客户端用没有任何参数的
        <a href="/commands/unsubscribe.html">UNSUBSCRIBE</a>命令取消订阅所有频道:</p>
        
        <pre><code>UNSUBSCRIBE&#x000A;*3&#x000A;$11&#x000A;unsubscribe&#x000A;$6&#x000A;second&#x000A;:1&#x000A;*3&#x000A;$11&#x000A;unsubscribe&#x000A;$5&#x000A;first&#x000A;:0&#x000A;</code></pre>
        
        <h2>模式匹配订阅</h2>
        
        <p>Redis 的Pub/Sub实现支持模式匹配。客户端可以订阅全风格的模式以便接收所有来自能匹配到给定模式的频道的消息。</p>
        
        <p>比如:</p>
        
        <pre><code>PSUBSCRIBE news.*&#x000A;</code></pre>
        
        <p>将接收所有发到news.art.figurative, news.music.jazz等等的消息，所有模式都是有效的，所以支持多通配符。</p>
        
        <pre><code>PUNSUBSCRIBE news.*&#x000A;</code></pre>
        
        <p>将取消订阅匹配该模式的客户端，这个调用不影响其他订阅。</p>
        
        <p>当作模式匹配结果的消息会以不同的格式发送:</p>
        
        <ul>
        <li>消息类型是pmessage:这是另一客户端发出的<a href="/commands/publish.html">PUBLISH</a>命令的结果,匹配一个模式匹配订阅。第一个元素是原匹配的模式，第三个元素是原频道名称，最后一个元素是实际消息内容。
       </li>
        </ul>
        
        
        <p>同样的，系统默认 <a href="/commands/subscribe.html">SUBSCRIBE</a> 和 <a href="/commands/unsubscribe.html">UNSUBSCRIBE</a>, <a href="/commands/psubscribe.html">PSUBSCRIBE</a> 和 <a href="/commands/punsubscribe.html">PUNSUBSCRIBE</a> 命令在发送 psubscribe 和punsubscribe类型的消息时使用像subscribe 和 unsubscribe一样的消息格式。  </p>
        
        <h2>同时匹配模式和频道订阅的消息</h2>
        
        <p>客户端可能多次接收一个消息，如果它订阅的多个模式匹配了同一个发布的消息，或者它订阅的模式和频道同时匹配到一个消息。就像下面的例子：</p>
        
        <pre><code>SUBSCRIBE foo&#x000A;PSUBSCRIBE f*&#x000A;</code></pre>
        
        <p>上面的例子中，如果一个消息被发送到foo,客户端会接收到两条消息：一条message类型，一条pmessage类型。</p>
        
        <h2>模式匹配统计的意义</h2>
        
        <p>在 subscribe, unsubscribe, psubscribe 和 punsubscribe 消息类型中，最后一个参数是依然活跃的订阅数。
这个数字是客户端依然订阅的频道和模式的总数。只有当退订频道和模式的数量下降到0时客户端才会退出Pub/Sub状态。</p>
        
        <h2>程序示例：</h2>
        
        <p>Peter Noordhuis 提供了一个使用EventMachine 和Redis创建多用户<a href="https://gist.github.com/348262">高性能网路聊天</a>的很棒的例子。</p>
        
        <h2>客户端库实现提示</h2>
        
        <p>因为所有接收到的消息包含原订阅导致消息传递（message类型时是频道，pmessage类型时是原模式）客户端库可能绑定原订阅到回调方法（可能是匿名函数，块，函数指针），使用hash table。当消息被接收的时候可以做到时间复杂度为O(1)的查找以便传递消息到已注册的回调。</p>
      </article>
    </div>
    <div class='text' id='comments'>
      <div id='disqus_thread'></div>
      <script type='text/javascript'>
        //<![CDATA[
          var disqus_shortname = 'rediscn';
          
          /* * * DON'T EDIT BELOW THIS LINE * * */
          (function() {
            var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
              dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
              (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
          })();
        //]]>
      </script>
      <a class='dsq-brlink' href='http://disqus.com'>
        Comments powered by
        <span class='logo-disqus'>
          Disqus
        </span>
      </a>
    </div>
    <script src='/js/foot.js'></script>
  </body>
</html>
